<template>
    <div v-show="startDate" class="gantt">
        <!-- 调整时间刻度 -->
        <div class="m-b-12" style="display: flex; align-items: center">
            <el-select :model-value="scale" style="width: 200px" @change="handleChange">
                <el-option
                    v-for="item in scaleList"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
                >
                </el-option>
            </el-select>
            <span
                v-if="router.fullPath === '/orderGantt'"
                style="margin-left: 10px; font-size: 14px; font-weight: 600; color: #8c8c8c"
                >颜色相同色块代表同一工序</span
            >
        </div>
        <!-- 甘特图 -->
        <div :style="{ height: contentHeight + 'px' }">
            <div ref="contentRef" class="content" :style="{ maxHeight: contentHeight + 'px' }">
                <!-- 左侧表格 -->
                <div class="content__left" :style="{ lineHeight: cellHeight + 'px' }">
                    <div class="content__left-bar" :style="{ width: barWidth + 'px' }">
                        <!-- 表格head -->
                        <div class="content__left-bar-head">
                            <div
                                v-for="(item, index) in gTableConfig"
                                :key="item.label + index"
                                :style="{
                                    height: cellHeight + 'px',
                                    width: (item.width ?? cellWidth) + 'px'
                                }"
                                class="content__left--title"
                            >
                                {{ item.label }}
                            </div>
                        </div>

                        <!-- 表格body -->
                        <div class="content__left-bar-table" @wheel="leftBarScroll">
                            <div ref="leftBarRef" style="display: flex">
                                <template
                                    v-for="(item, index) in gTableConfig"
                                    :key="item.label + index"
                                >
                                    <left-bar
                                        style="flex: 1"
                                        :data="ganttData"
                                        :cell-height="cellHeight"
                                        :cell-width="
                                            leftWidth(index === gTableConfig.length - 1, item.width)
                                        "
                                        :class="{ borderL: index === gTableConfig.length - 1 }"
                                    >
                                        <template #default="{ rowData }">
                                            <!-- <template v-if="!rowData.expand"> -->
                                            <div style="display: flex">
                                                <span
                                                    style="
                                                        padding-right: 3px;
                                                        font-weight: 800;
                                                        cursor: pointer;
                                                    "
                                                    @click.stop="toggle(rowData)"
                                                >
                                                    <q-icon
                                                        v-if="
                                                            (rowData.hide &&
                                                                item.prop &&
                                                                item.prop === 'orderNO' &&
                                                                rowData.node) ||
                                                            (rowData.hide &&
                                                                item.prop &&
                                                                item.prop === 'materialCode' &&
                                                                rowData.children)
                                                        "
                                                        icon-class="xiala"
                                                        size="14"
                                                        style="margin-bottom: 1px"
                                                    />

                                                    <q-icon
                                                        v-else-if="
                                                            (rowData.hide === false &&
                                                                item.prop &&
                                                                item.prop === 'orderNO' &&
                                                                rowData.node) ||
                                                            (rowData.hide === false &&
                                                                item.prop &&
                                                                item.prop === 'materialCode' &&
                                                                rowData.children)
                                                        "
                                                        style="margin-bottom: 2px"
                                                        icon-class="zhankai1"
                                                        size="10"
                                                    />
                                                </span>
                                                <span
                                                    v-if="
                                                        item.prop &&
                                                        item.prop === 'materialCode' &&
                                                        !rowData.children &&
                                                        !rowData.node
                                                    "
                                                    style="margin-right: 10%"
                                                ></span>
                                                <span>{{ rowData[item.prop] }}</span>
                                            </div>
                                            <!-- <slot
                                                :name="item.prop"
                                                :tableData="{ rowData: rowData[item.prop] }"
                                            >
                                                <q-tooltip :content="rowData[item.prop]" />
                                            </slot> -->
                                            <!-- </template> -->
                                        </template>
                                    </left-bar>
                                </template>
                            </div>
                        </div>

                        <!-- 表格展开收起 -->
                        <div class="content__left-bar--trapezoid" @click="barOpen">
                            <q-icon size="50px" :icon-class="isOpen ? 'suofang' : 'zhankai3'" />
                        </div>
                    </div>
                </div>

                <!-- 右侧甘特图 -->
                <div class="content__right">
                    <div v-if="startDate" class="gantt-header-timeline">
                        <div
                            ref="headerTimeline"
                            class="gantt-header-timeline-container"
                            :style="{ width: totalWidth + 'px' }"
                        >
                            <!-- 时间线 -->
                            <time-line
                                ref="timeLineRef"
                                :data="ganttData"
                                :start="dayjs(time[0])"
                                :end="dayjs(time[1])"
                                :cellWidth="cellWidth"
                                :titleHeight="cellHeight"
                                :scale="scale"
                                :s-render-time="dayjs(SRenderTime)"
                                :e-render-time="dayjs(ERenderTime)"
                                :getPositionOffset="handleOffsetOptions"
                            >
                                <template #default="{ times }">
                                    <slot name="timeCol" :times="times"></slot>
                                </template>
                            </time-line>
                        </div>
                    </div>

                    <div id="iscroll" ref="wrapperRef" class="gantt-blocks-wrapper">
                        <!-- 甘特图 -->
                        <div
                            class="scroller"
                            :style="{ width: startDate ? `${totalWidth}px` : '100%' }"
                        >
                            <gantt-group
                                :data="ganttData"
                                :start-time="dayjs(time[0])"
                                :end-time="dayjs(time[1])"
                                :cell-height="cellHeight"
                                :cell-width="cellWidth"
                                :scale="scale"
                                :e-render-time="ERenderTime"
                                :s-render-time="SRenderTime"
                                :un-visible-h="unVisibleH"
                                :wrapper-h="wrapperH"
                                :wrapper-t="wrapperT"
                                :scroll-top="scrollTop"
                            >
                                <template #BlockRow="{ rowData, showList }">
                                    <gantt-row
                                        :show-col-list="showList"
                                        :row-item="rowData"
                                        :cell-height="cellHeight"
                                        :style="{
                                            height: cellHeight + 'px',
                                            lineHeight: cellHeight + 'px'
                                        }"
                                        :get-position-offset="handleOffsetOptions"
                                        :get-scope-width="handleScopeWidth"
                                        @dragover.prevent
                                        @drop.stop="dropToRow($event, rowData)"
                                        @getCurrentItem="getCurrentItem"
                                    >
                                        <template #colList="{ colList }">
                                            <gantt-col
                                                v-for="(col, index) in colList"
                                                :key="index"
                                                :dataClor="dataClor"
                                                :col="col"
                                                :rowItem="rowData"
                                                :index="index"
                                                :get-position-offset="handleOffsetOptions"
                                                :get-scope-width="handleScopeWidth"
                                                @dragover.prevent
                                                @mousedown.left.stop="
                                                    leftClick($event, col, rowData)
                                                "
                                            >
                                                <template #col="{ colData }">
                                                    <slot name="col" :colData="colData" />
                                                </template>
                                            </gantt-col>
                                        </template>
                                    </gantt-row>
                                </template>
                            </gantt-group>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <!-- 分页 -->
        <el-row ref="paginationRef" justify="end" class="gantt__pagination">
            <el-pagination
                :current-page="page.current"
                :page-size="page.size"
                :total="total"
                background
                layout="total, slot, sizes, prev, pager, next"
                :page-sizes="[10, 20, 50, 100]"
                @size-change="sizeChange"
                @current-change="currentChange"
            >
                <template #default>
                    <q-icon
                        icon-class="gengxinqi"
                        size="16px"
                        :class="['u-pagination-icon', { 'is-active': loading }]"
                        @click="handleRefresh"
                    ></q-icon>
                </template>
            </el-pagination>
        </el-row>
    </div>
    <q-empty v-show="!startDate"></q-empty>
</template>

<script setup lang="ts">
import dayjs from 'dayjs';

import ganttGroup from './components/gantt-group.vue';
import timeLine from './components/time-line.vue';
import leftBar from './components/left-bar.vue';
import useDrop from './hooks/useDrop';
import useObserver from './hooks/useObserver';
import useScroller from './hooks/useInitScroller';
import useHeight from './hooks/useHeight';
import useGetData from './hooks/useGetData';
import { useMouse } from './hooks/useMouse';
import { calcScalesAboutTimes, getstartOfTime } from './utils/timeLineUtils';
import { getPositionOffset, getScopeWidth, mapPositionToTime } from './utils/getUtils';
import { scaleList as scaleOption } from './data';
import type { GTableConfig } from './type';
import type { Result } from '@/service/request/type';
import type { Records } from '@/api/plan/gantt/model/type';
import { Message } from '@/instance';

const router = useRoute();
const props = withDefaults(
    defineProps<{
        dataClor: string;
        cellHeight?: number;
        cellWidth?: number;
        scaleList?: {
            value: number;
            label: string;
        }[];
        gTableConfig: GTableConfig[];
        getDataFn: (params: any) => Promise<Result>;
    }>(),
    {
        cellHeight: 50, // 单元格高度
        cellWidth: 100, // 单元格宽度
        scaleList: () => scaleOption //时间选择下拉列表
    }
);

// 获取数据
const {
    page,
    time,
    total,
    loading,
    tableData,
    sizeChange,
    handleRefresh,
    currentChange,
    getData,
    startDate
} = useGetData(props.getDataFn);

/**
 * 递归遍历表格数据
 */
const extractMaterialCodes = (objArray: Records[] = [], resultArray: number[] = []) => {
    objArray.forEach(obj => {
        resultArray.push(obj.id!);
        if (obj.children && obj.children.length > 0) {
            extractMaterialCodes(obj.children ?? [], resultArray);
        }
    });
    return resultArray;
};
/**
 * 列表控制函数
 */
const controlTable = (data: number[], hide: boolean) => {
    let ind = 0;
    ganttData.value.forEach(obj => {
        //没有在匹配字段的数据处理
        if (!data.includes(obj.id!)) {
            if (!obj.hideRow) {
                obj.rowIndex = ind;
                ind++;
            }
            //在匹配字段的数据处理
        } else {
            obj.hideRow = hide;
            obj.hide = true;
            if (obj.hideRow) {
                obj.rowIndex = 0;
            } else {
                obj.rowIndex = ind;
                ind++;
            }
        }
    });
    ind = 0;
};

/**
 * 点击列表图标事件
 */
const toggle = (rowData: Records) => {
    rowData.hide = !rowData.hide;
    //点击订单号的控制
    if (rowData.node) {
        //拿到唯一值
        const dataVal = extractMaterialCodes(rowData.node.children);
        const data = [rowData.node.id!, ...dataVal];
        controlTable(data, !rowData.hide);
    }
    //点击物料编码的控制
    if (rowData.children) {
        const dataVal = extractMaterialCodes(rowData.children);
        controlTable(dataVal, !rowData.hide);
    }
};

/**
 * @: 通过位置计算出时间
 */
const handleMapTime = (xStart: number) => {
    const options = { scale: scale.value, cellWidth: props.cellWidth };
    const timeString = time.value[0].toDateString();
    return mapPositionToTime(xStart, timeString, options);
};

// 初始化滚动组件相关
const { scroller, timeLineRef, leftBarRef, scrollLeft, scrollTop, scrollRefresh } = useScroller();

// 视口宽高距离相关
const { wrapperRef, wrapperH, wrapperW, wrapperT, handleObserve } = useObserver();

/**
 * @: 刷新
 */
const refreshWrapper = async (isRefreshScroll = true) => {
    let num = isOpen.value ? 1 : 0;
    await nextTick();
    handleObserve();
    // 移动1px用来刷新滚动
    scroller.value.scrollTo(scroller.value.x + num, scroller.value.y, 100);
    if (isRefreshScroll) scrollRefresh();
};

// 动态计算高度
const { contentRef, paginationRef, contentHeight } = useHeight(refreshWrapper);

/**
 * @: 初始化甘特图
 */
const init = () => {
    scroller.value.scrollTo(0, 0, 100);
    handleObserve();
    scrollRefresh();
};

// 拖动相关
const { getCurrentItem, ganttData, dropToRow, leftClick } = useDrop(handleMapTime, tableData, init);

// 时间刻度
const scale = ref(180);

/**
 * @: 全部刻度的个数
 */
const totalScales = computed(() => {
    const [start, end] = time.value;
    return calcScalesAboutTimes(dayjs(start), dayjs(end), scale.value);
});

// 全部宽度
const totalWidth = computed(() => props.cellWidth * totalScales.value);

// 移动相关
useMouse(timeLineRef, scroller, scrollLeft, totalWidth, wrapperW);

// 开始时间的位置
const beginTime = computed(() => getstartOfTime(dayjs(time.value[0]), scale.value));

// 剩余区域的高度
const unVisibleH = computed(() => window.innerHeight - wrapperH.value);

/**
 * @: 渲染区域的开始时间
 */
const SRenderTime = computed(() => {
    return beginTime.value
        .add((scrollLeft.value / props.cellWidth) * scale.value, 'minute')
        .toDate()
        .getTime();
});

/**
 * @: 渲染区域的结束时间
 */
const ERenderTime = computed(() => {
    return beginTime.value
        .add(((scrollLeft.value + wrapperW.value) / props.cellWidth) * scale.value, 'minute')
        .toDate()
        .getTime();
});

/**
 * @: 切换时间后跳转到中间
 */
let centerTime: number; //中间时间
const scrollToCenter = async () => {
    // 甘特图可视区域除以2
    let width = handleScopeWidth(SRenderTime.value, ERenderTime.value) / 2;
    let startOffset = handleOffsetOptions(centerTime);
    const offset = -startOffset + width > 0 ? 0 : -startOffset + width;
    scroller.value.scrollTo(offset, scroller.value.y);
    await refreshWrapper(false);
    await nextTick(() => {
        scroller.value?.refresh();
    });
};

/**
 * @: 切换时间区间
 */
const handleChange = async (e: number) => {
    // 拿到更改之前的渲染区域时间
    centerTime = SRenderTime.value + (ERenderTime.value - SRenderTime.value) / 2;
    scale.value = e;
    scrollToCenter();
};

/**
 * @: 获取传入时间到开始时间的距离
 */
const handleOffsetOptions = (date: string | number) => {
    const options = { scale: scale.value, cellWidth: props.cellWidth };
    return getPositionOffset(date, beginTime.value.toString(), options);
};

/**
 * @: 获取传入的开始到结束宽度
 */
const handleScopeWidth = (start: string | number, end: string | number) => {
    const options = { scale: scale.value, cellWidth: props.cellWidth };
    return getScopeWidth(start, end, options);
};

/**
 * @:侧边栏宽度
 */
const barWidth = computed(() => {
    return props.gTableConfig.reduce((total, item) => {
        return total + (item.width || props.cellWidth);
    }, 0);
});

// 侧边栏展开收起
const emit = defineEmits(['openTable']);

/**
 * @: 打开侧边栏相关
 */
const isOpen = ref(true);
const ERenderW = ref(barWidth.value);
const barOpen = () => {
    isOpen.value = !isOpen.value;
    if (isOpen.value) {
        ERenderW.value = barWidth.value;
    } else {
        ERenderW.value = 0;
    }
    emit('openTable', isOpen.value);
    setTimeout(() => {
        refreshWrapper();
    }, 120);
};

/**
 * @:表格最右边的列需要减去border的宽度
 */
const leftWidth = (isLast: boolean, width: number | undefined) => {
    if (isLast) {
        return (width ?? props.cellWidth) - 1;
    } else {
        return width ?? props.cellWidth;
    }
};

/**
 * @: 左侧滚动
 */
const debounceMsg = useThrottleFn(() => {
    Message.error('已经滚动到底部');
}, 3100);

const leftBarScroll = (e: any) => {
    const { x, y, maxScrollY } = scroller.value;
    const time = setTimeout(() => {
        if (e.wheelDelta > 0 && 0 > y) {
            const scroll = y + 100 > 0 ? 0 : y + 100;
            if (y === 0 && scroll === y) return;
            scroller.value.scrollTo(x, scroll, 20);
        }

        if (e.wheelDelta < 0 && y <= 0) {
            if (maxScrollY === y) {
                debounceMsg();
            }
            const childHeight = leftBarRef.value?.getBoundingClientRect().height || 0;
            const parentHeight = leftBarRef.value?.parentElement?.offsetHeight || 0;
            const wheel = -(childHeight - parentHeight + 4);
            if (y === wheel) return;
            const scroll = y - 100 <= wheel ? wheel : y - 100;
            scroller.value.scrollTo(x, scroll, 20);
        }
        clearTimeout(time);
    }, 100);
};

defineExpose({
    getData,
    getDataFn: props.getDataFn
});
</script>

<style scoped lang="scss">
@include b(gantt) {
    width: 100%;
    height: 100%;

    @include e(pagination) {
        padding-top: 14px;
    }
}

@include b(content) {
    display: flex;
    width: 100%;

    @include e(left) {
        display: flex;
        @include m(title) {
            position: relative;
            z-index: 2;
            box-sizing: border-box;
            padding: 0 10px;
            font-weight: 700;
            color: var(--q-text-color-secondary);
            background-color: var(--q-bg-color-secondary);

            @include utils-ellipsis(1);
        }
    }

    @include e(left-bar) {
        position: relative;
        font-size: 14px;
        transition: all 0.2s;

        @include m(trapezoid) {
            position: absolute;
            top: 46%;
            right: 8px;
            width: 24px;
            height: 52px;
            cursor: pointer;
        }
    }

    @include e(left-bar-head) {
        position: relative;
        z-index: 5;
        display: flex;

        &::after {
            position: absolute;
            top: 0;
            right: 0;
            bottom: 0;
            width: 1px;
            content: '';
            box-shadow: 1px 0 6px 0 var(--q-text-color-status);
        }
    }

    @include e(left-bar-table) {
        height: calc(100% - 46px);
        overflow: hidden;
    }

    @include e(right) {
        flex: 1;
        width: 0;
        overflow: hidden;
    }

    :deep(.iScrollVerticalScrollbar) {
        width: var(--q-scrollbar-width) !important;
    }

    :deep(.iScrollHorizontalScrollbar) {
        height: var(--q-scrollbar-width) !important;
    }

    :deep(.iScrollIndicator) {
        background-color: var(--q-bg-scrollbar-thumb) !important;
        border: none !important;
    }
}

@include b(gantt-blocks-wrapper) {
    position: relative;
    height: calc(100% - 48px);
    overflow: hidden;
    user-select: none;
    border: 1px solid var(--q-color-info-light-9);
    border-top: transparent;
    border-left: transparent;
}

.borderL {
    border-right: 1px solid var(--q-color-info-light-8);
}

@include b(u-pagination-icon) {
    margin-left: 12px;
    cursor: pointer;

    @include when('active') {
        animation: rotation 1s linear infinite;
    }
}
</style>
